﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DevExpress.XtraEditors;
using DevExpress.XtraGrid.Views.Grid;
using System.Data;
using System.ComponentModel;
using DevExpress.XtraGrid.Columns;
using DevExpress.XtraGrid.Views.Base;
using ProgNetComponentsDx.Base.Components;
using ProgNetComponentsDx.Controls.Editors;
using DevExpress.XtraEditors.Repository;
using System.Windows.Forms;

namespace ProgNetComponentsDx.ChangeDetect
{
    public class ChangeDetectorProvider : BaseExtenderProvider<ChangeDetectSettings>
    {
        public ChangeDetectorProvider() { }
        public ChangeDetectorProvider(IContainer container) : base(container) { }

        public event EventHandler ContextChanged;

        private Dictionary<object, object> Values = new Dictionary<object, object>();
        public bool HasChanges
        {
            get { return Values.Count > 0 || _IsModified; }
        }
        private bool _IsModified = false;
        public void SetModified()
        {
            if (!_IsModified)
            {
                _IsModified = true;
                OnContextChanged();
            }
        }

        protected override void ProcessObject(object target, ChangeDetectSettings settings)
        {
            if (target is BaseEdit)
            {
                (target as BaseEdit).EditValueChanging -= new DevExpress.XtraEditors.Controls.ChangingEventHandler(ChangeDetectorProvider_EditValueChanging);
                if (settings.DetectChanges)
                    (target as BaseEdit).EditValueChanging += new DevExpress.XtraEditors.Controls.ChangingEventHandler(ChangeDetectorProvider_EditValueChanging);
            }
            else if(target is GridView)
            {
                (target as GridView).CellValueChanging -= new DevExpress.XtraGrid.Views.Base.CellValueChangedEventHandler(ChangeDetectorProvider_CellValueChanging);
                (target as GridView).RowCountChanged -= new EventHandler(ChangeDetectorProvider_RowCountChanged);
                
                if (settings.DetectChanges)
                {
                    (target as GridView).CellValueChanging += new DevExpress.XtraGrid.Views.Base.CellValueChangedEventHandler(ChangeDetectorProvider_CellValueChanging);
                    (target as GridView).RowCountChanged += new EventHandler(ChangeDetectorProvider_RowCountChanged);
                }
            }
            else if (target is GridColumn)
            {
                GridColumn col = target as GridColumn;
                if (col.ColumnEdit != null && col.ColumnEdit is RepositoryItemMultiSelectGridEdit)
                {
                    ProcessObject(col.ColumnEdit, GetSettings(col.ColumnEdit));
                }
            }
            else if (target is RepositoryItemMultiSelectGridEdit)
            {
                if (target is RepositoryItemMultiSelectGridEdit)
                {
                    RepositoryItemMultiSelectGridEdit rep = (target as RepositoryItemMultiSelectGridEdit);
                    rep.Tag = rep;
                }
                (target as RepositoryItemMultiSelectGridEdit).RowModified -= new DataRowHandler(ChangeDetectorProvider_RowModified);
                if (settings.DetectChanges)
                {
                    (target as RepositoryItemMultiSelectGridEdit).RowModified += new DataRowHandler(ChangeDetectorProvider_RowModified);
                }
            }
            else if (target is CheckedListBoxControl)
            {
                (target as CheckedListBoxControl).ItemChecking -= new DevExpress.XtraEditors.Controls.ItemCheckingEventHandler(ChangeDetectorProvider_ItemChecking);
                if (settings.DetectChanges)
                    (target as CheckedListBoxControl).ItemChecking += new DevExpress.XtraEditors.Controls.ItemCheckingEventHandler(ChangeDetectorProvider_ItemChecking);
            }
        }

        void ChangeDetectorProvider_ItemChecking(object sender, DevExpress.XtraEditors.Controls.ItemCheckingEventArgs e)
        {
            CheckedListBoxControl c = (sender as CheckedListBoxControl);
            if (c != null)
            {
                CheckChanges(c, new CheckedListBoxItemInfo(e.Index, e.OldValue), new CheckedListBoxItemInfo(e.Index, e.NewValue));
            }
        }
        private class CheckedListBoxItemInfo
        {
            public CheckedListBoxItemInfo(int index, CheckState state)
            {
                Index = index;
                CheckState = state;
            }
            public int Index { get; set; }
            public CheckState CheckState
            {
                get;
                set;
            }
            public override bool Equals(object obj)
            {
                CheckedListBoxItemInfo o = obj as CheckedListBoxItemInfo;
                if (o != null)
                {
                    return o.Index == Index &&
                        o.CheckState == CheckState;
                }
                return false;
            }
            public override int GetHashCode()
            {
                return Index.GetHashCode() + CheckState.GetHashCode();
            }
        }

        void ChangeDetectorProvider_RowModified(object sender, DataRowEventArgs e)
        {
            if (e.Row != null)
            {
                if (e.Row.Table.GetChanges() != null)
                {
                    if (!Values.ContainsKey(e.Row.Table))
                        Values.Add(e.Row.Table, true);
                }
                else
                {
                    if (Values.ContainsKey(e.Row.Table))
                        Values.Remove(e.Row.Table);
                }
                OnContextChanged();
            }
        }

        void ChangeDetectorProvider_RowCountChanged(object sender, EventArgs e)
        {
            GridView v = sender as GridView;
            if (v != null)
            {
                DataTable dt = v.GetDataTable();
                if (dt != null)
                {
                    bool hasAddedInsertedRows = dt.Select(null, null, DataViewRowState.Added | DataViewRowState.Deleted).Length > 0;
                    CheckChanges(dt, false, hasAddedInsertedRows);
                }
            }
        }

        void ChangeDetectorProvider_CellValueChanging(object sender, DevExpress.XtraGrid.Views.Base.CellValueChangedEventArgs e)
        {
            GridView v = sender as GridView;
            if (v != null)
            {
                object key = GetRealKey(e.Column);
                if (key !=null && AllObjects.ContainsKey(key))
                {
                    if (!AllObjects[key].DetectChanges)
                        return;
                }
                if (e.RowHandle == DevExpress.XtraGrid.GridControl.NewItemRowHandle ||
                    e.RowHandle == DevExpress.XtraGrid.GridControl.AutoFilterRowHandle)
                    return;
                if (v.IsValidRowHandle(e.RowHandle))
                {
                    DataRow r = v.GetDataRow(e.RowHandle);
                    if (r != null && (r.RowState == DataRowState.Added || r.RowState == DataRowState.Added))
                        return;
                }
                string uniqueName = v.Name + "_" + e.Column.FieldName + "[" + e.RowHandle + "]";
                object oldValue = v.GetRowCellValue(e.RowHandle, e.Column);
                
                CheckChanges(uniqueName, oldValue, e.Value);
            }
        }

        void ChangeDetectorProvider_EditValueChanging(object sender, DevExpress.XtraEditors.Controls.ChangingEventArgs e)
        {
            CheckChanges(sender, e.OldValue, e.NewValue);
        }

        public void CheckChanges(object target, object oldValue, object newValue)
        {
            if (Values.ContainsKey(target))
            {
                if (Values[target].IsTheSame(newValue))
                    Values.Remove(target);
            }
            else
            {
                if (!oldValue.IsTheSame(newValue))
                    Values.Add(target, oldValue);
            }
            OnContextChanged();
        }

        protected override object GetRealKey(object target)
        {
            object key = target;

            if (target is GridColumn)
            {
                ColumnView cv = (target as GridColumn).View as ColumnView;

                if (cv != null && cv.SourceView != null && cv.SourceView is ColumnView)
                {
                    cv = cv.SourceView as ColumnView;
                    try
                    {
                        key = cv.Columns.ColumnByName((target as GridColumn).Name);
                    }
                    catch { }
                }
            }
            if (target is BaseView)
            {
                BaseView gv = target as BaseView;
                if (gv.SourceView != null)
                    key = gv.SourceView;
            }
            else if (target is RepositoryItem &&
               (target as RepositoryItem).Tag != null &&
               (target as RepositoryItem).Tag is RepositoryItem)
                return (target as RepositoryItem).Tag as RepositoryItem;
            return key;
        }

        protected override ChangeDetectSettings CreateSettings(object target)
        {
            return new ChangeDetectSettings(target, this);
        }

        public override bool CanExtend(object extendee)
        {
            if (extendee is BaseEdit ||
                extendee is GridView ||
                extendee is GridColumn ||
                extendee is RepositoryItemMultiSelectGridEdit || 
                extendee is CheckedListBoxControl)
                return true;
            return false;
        }

        protected void OnContextChanged()
        {
            if (!this.IsDesignMode())
            {
                if (ContextChanged != null)
                    ContextChanged(this, EventArgs.Empty);
            }
        }

        public void ResetAll()
        {
            Values.Clear();
            _IsModified = false;
            OnContextChanged();
        }
    }
}
